"""基准策略."""

from __future__ import annotations

import numpy as np
import pandas as pd

from .config import BacktestConfig
from .metrics import BacktestMetrics


class BuyAndHoldStrategy:
    """买入并持有基准策略."""

    def __init__(self, config: BacktestConfig) -> None:
        self.config = config

    def calculate_returns(
        self,
        prices: pd.Series,
        start_date: pd.Timestamp | None = None,
        end_date: pd.Timestamp | None = None,
    ) -> pd.Series:
        """计算买入并持有策略的收益率序列.

        Parameters
        ----------
        prices:
            价格序列（收盘价）
        start_date:
            起始日期（可选）
        end_date:
            结束日期（可选）

        Returns
        -------
        日收益率序列
        """
        if start_date is not None:
            prices = prices[prices.index >= start_date]
        if end_date is not None:
            prices = prices[prices.index <= end_date]

        returns = prices.pct_change().dropna()
        return returns

    def calculate_metrics(
        self,
        prices: pd.Series,
        start_date: pd.Timestamp | None = None,
        end_date: pd.Timestamp | None = None,
    ) -> BacktestMetrics:
        """计算买入并持有策略的评估指标.

        Parameters
        ----------
        prices:
            价格序列（收盘价）
        start_date:
            起始日期（可选）
        end_date:
            结束日期（可选）

        Returns
        -------
        评估指标
        """
        returns = self.calculate_returns(prices, start_date, end_date)

        if len(returns) == 0:
            return BacktestMetrics(
                cumulative_return=0.0,
                total_return=0.0,
                annualized_return=0.0,
                annualized_volatility=0.0,
                downside_volatility=0.0,
                sharpe_ratio=0.0,
                sortino_ratio=0.0,
                calmar_ratio=0.0,
                max_drawdown=0.0,
                win_rate=0.0,
                avg_gain=0.0,
                avg_loss=0.0,
                gain_loss_ratio=0.0,
                information_ratio=0.0,
                tracking_error=0.0,
                excess_return=0.0,
                total_trades=0,
                avg_holding_period=0.0,
                stop_loss_count=0,
                take_profit_count=0,
            )

        # 计算净值曲线
        equity_curve = (1 + returns).cumprod() * self.config.initial_capital

        # 总收益率
        total_return = (equity_curve.iloc[-1] - equity_curve.iloc[0]) / equity_curve.iloc[0]

        # 年化收益率
        n_days = len(returns)
        annualized_return = (1 + total_return) ** (252 / n_days) - 1

        # 年化波动率
        annualized_volatility = returns.std() * np.sqrt(252)

        # 夏普/Sortino
        if annualized_volatility > 0:
            sharpe_ratio = (annualized_return - self.config.risk_free_rate) / annualized_volatility
        else:
            sharpe_ratio = 0.0

        downside_returns = returns[returns < 0]
        if len(downside_returns) > 0:
            downside_volatility = downside_returns.std() * np.sqrt(252)
        else:
            downside_volatility = 0.0
        sortino_ratio = (
            (annualized_return - self.config.risk_free_rate) / downside_volatility
            if downside_volatility > 0
            else 0.0
        )

        # 最大回撤
        cummax = equity_curve.cummax()
        drawdown = (equity_curve - cummax) / cummax
        max_drawdown = float(drawdown.min())
        calmar_ratio = (
            annualized_return / abs(max_drawdown) if abs(max_drawdown) > 1e-9 else 0.0
        )

        # 胜率
        positive_returns = returns[returns > 0]
        win_rate = len(positive_returns) / len(returns) if len(returns) > 0 else 0.0

        return BacktestMetrics(
            cumulative_return=float(total_return),
            total_return=float(total_return),
            annualized_return=float(annualized_return),
            annualized_volatility=float(annualized_volatility),
            downside_volatility=float(downside_volatility),
            sharpe_ratio=float(sharpe_ratio),
            sortino_ratio=float(sortino_ratio),
            calmar_ratio=float(calmar_ratio),
            max_drawdown=float(max_drawdown),
            win_rate=float(win_rate),
            avg_gain=0.0,
            avg_loss=0.0,
            gain_loss_ratio=0.0,
            information_ratio=0.0,
            tracking_error=0.0,
            excess_return=0.0,
            total_trades=1,  # 买入并持有只有一次交易
            avg_holding_period=float(n_days),
            stop_loss_count=0,
            take_profit_count=0,
        )

